//
// Created by twetec on 25-8-16.
//

#include "../include/network_prober/probe_detect/handle_tcp_probe_detect.h"

#include <boost/json.hpp>
#include <boost/algorithm/string.hpp>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <bs_thread_pool/BS_thread_pool.hpp>

using namespace std::literals::chrono_literals;


namespace network_prober::probe_detect
{
// 线程池
static BS::thread_pool s_iopool{200};

std::ostream& operator<<(std::ostream& out, const TCPProbeDetectResultIPv4& result)
{
    out
        << "interface: " << result.interface
        << " & target: " << result.target
        << " & port: " << result.port
        << " & delay: " << result.delay;

    return out;
}

void tag_invoke(boost::json::value_from_tag, boost::json::value &jv, TCPProbeDetectResultIPv4 const &c)
{
    auto& ja = jv.emplace_object();

    ja["interface"] = c.interface;
    ja["target"] = c.target;
    ja["port"] = c.port;
    ja["delay"] = c.delay;
}

HandleTCPProbeDetectIPv4::HandleTCPProbeDetectIPv4(const std::string &interface, const std::string &target)
    : m_interface(interface)
{
    // 将设备ip,按照逗号分隔符解析出来
    std::vector<std::string> targetAndPort;
    boost::split(targetAndPort, target, boost::is_any_of(":"), boost::token_compress_on);

    // 目标地址
    m_target = targetAndPort.at(0);
    // 端口号
    m_port = targetAndPort.at(1);
}

HandleTCPProbeDetectIPv4::HandleTCPProbeDetectIPv4(const std::string &interface, const std::string &target, std::uint32_t port)
    : m_interface(interface), m_target(target), m_port(std::to_string(port))
{
}

void HandleTCPProbeDetectIPv4::setOnDetectedCallback(std::function<void(TCPProbeDetectResultIPv4 &&)> callback)
{
    m_onDetectedCallback = std::move(callback);
}

void HandleTCPProbeDetectIPv4::detect()
{
    // 指向当前对象的shared_ptr
    auto self{shared_from_this()};

    // 发起探测
    s_iopool.submit_task([this, self] {
        // 构建探测结果
        TCPProbeDetectResultIPv4 result {
            .interface = m_interface,
            .target = m_target,
            .port = std::stoul(m_port),
            .delay = -1
        };

        for (std::size_t i = 0; i < 3; i ++) {
            // 创建UDP套接字
            int sockfd = socket(AF_INET, SOCK_STREAM, 0);
            if (sockfd < 0) {
                // 输出日志
                SPDLOG_INFO("sockfd创建失败: {}", strerror(errno));
                // 继续申请套接字
                continue;
            }

            // 设置接收超时(5s)
            struct timeval tv;
            tv.tv_sec = 3000 / 1000;
            tv.tv_usec = (3000 % 1000) * 1000;
            if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("sockfd: {} - 超时设置时间失败: {}", sockfd, strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束循环
                break;
            }

            // 设定网口地址
            struct sockaddr_in interfaceAddr;
            memset(&interfaceAddr, 0, sizeof(interfaceAddr));
            interfaceAddr.sin_family = AF_INET;
            interfaceAddr.sin_port = htons(0);
            if (inet_pton(AF_INET, m_interface.c_str(), &interfaceAddr.sin_addr) <= 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("sockfd: {} - 网口地址设置失败: {}", sockfd, strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束循环
                break;
            }

            // 绑定本机网口
            if (bind(sockfd, (sockaddr *)&interfaceAddr, sizeof(interfaceAddr)) < 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("sockfd: {} - 网口地址绑定失败: {}", sockfd, strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束循环
                break;
            }

            // 设置目标地址
            struct sockaddr_in targetAddr;
            memset(&targetAddr, 0, sizeof(targetAddr));
            targetAddr.sin_family = AF_INET;
            targetAddr.sin_port = htons(std::stoul(m_port));
            if (inet_pton(AF_INET, m_target.c_str(), &targetAddr.sin_addr) <= 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("sockfd: {} - 目标地址设置失败: {}", sockfd, strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束循环
                break;
            }

            // 建立连接时，开始计时
            std::chrono::time_point<std::chrono::steady_clock> detectStartTime = std::chrono::steady_clock::now();

            // 连接tcp服务端
            if (connect(sockfd, (struct sockaddr *)&targetAddr, sizeof(targetAddr)) < 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("服务器连接失败: {}", strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束循环
                break;
            }

            // 关闭socket
            close(sockfd);

            // 接收到数据，计算延迟
            // 设置延迟
            result.delay = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - detectStartTime).count();
            // 结束循环
            break;
        }

        // 回调上报
        m_onDetectedCallback(std::move(result));
    });
}

HandleTCPProbeDetectIPv4::~HandleTCPProbeDetectIPv4()
{
}

std::ostream& operator<<(std::ostream& out, const TCPProbeDetectResultIPv6& result)
{
    out
        << "interface: " << result.interface
        << " & target: " << result.target
        << " & port: " << result.port
        << " & delay: " << result.delay;

    return out;
}

void tag_invoke(boost::json::value_from_tag, boost::json::value &jv, TCPProbeDetectResultIPv6 const &c)
{
    auto& ja = jv.emplace_object();

    ja["interface"] = c.interface;
    ja["target"] = c.target;
    ja["port"] = c.port;
    ja["delay"] = c.delay;
}

HandleTCPProbeDetectIPv6::HandleTCPProbeDetectIPv6(const std::string &interface, const std::string &target)
    : m_interface(interface)
{
    // 将设备ip,按照逗号分隔符解析出来
    std::vector<std::string> targetAndPort;
    boost::split(targetAndPort, target, boost::is_any_of(":"), boost::token_compress_on);

    // 目标地址
    m_target = targetAndPort.at(0);
    // 端口号
    m_port = targetAndPort.at(1);
}

HandleTCPProbeDetectIPv6::HandleTCPProbeDetectIPv6(const std::string &interface, const std::string &target, std::uint32_t port)
    : m_interface(interface), m_target(target), m_port(std::to_string(port))
{
}

void HandleTCPProbeDetectIPv6::setOnDetectedCallback(std::function<void(TCPProbeDetectResultIPv6 &&)> callback)
{
    m_onDetectedCallback = std::move(callback);
}

void HandleTCPProbeDetectIPv6::detect()
{
    // 指向当前对象的shared_ptr
    auto self{shared_from_this()};

    // 发起探测
    s_iopool.submit_task([this, self] {
        // 构建探测结果
        TCPProbeDetectResultIPv6 result {
            .interface = m_interface,
            .target = m_target,
            .port = std::stoul(m_port),
            .delay = -1
        };

        for (std::size_t i = 0; i < 3; i ++) {
            // 创建UDP套接字
            int sockfd = socket(AF_INET6, SOCK_STREAM, 0);
            if (sockfd < 0) {
                // 输出日志
                SPDLOG_INFO("sockfd创建失败: {}", strerror(errno));
                // 继续申请套接字
                continue;
            }

            // 设置接收超时(5s)
            struct timeval tv;
            tv.tv_sec = 3000 / 1000;
            tv.tv_usec = (3000 % 1000) * 1000;
            if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("sockfd: {} - 超时设置时间失败: {}", sockfd, strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束循环
                break;
            }

            // 设定网口地址
            struct sockaddr_in6 interfaceAddr;
            memset(&interfaceAddr, 0, sizeof(interfaceAddr));
            interfaceAddr.sin6_family = AF_INET6;
            interfaceAddr.sin6_port = htons(0);
            if (inet_pton(AF_INET6, m_interface.c_str(), &interfaceAddr.sin6_addr) <= 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("sockfd: {} - 网口地址设置失败: {}", sockfd, strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束循环
                break;
            }

            // 绑定本机网口
            if (bind(sockfd, (sockaddr *)&interfaceAddr, sizeof(interfaceAddr)) < 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("sockfd: {} - 网口地址绑定失败: {}", sockfd, strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束循环
                break;
            }

            // 设置目标地址
            struct sockaddr_in6 targetAddr;
            memset(&targetAddr, 0, sizeof(targetAddr));
            targetAddr.sin6_family = AF_INET6;
            targetAddr.sin6_port = htons(std::stoul(m_port));
            if (inet_pton(AF_INET6, m_target.c_str(), &targetAddr.sin6_addr) <= 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("sockfd: {} - 目标地址设置失败: {}", sockfd, strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束循环
                break;
            }

            // 建立连接时，开始计时
            std::chrono::time_point<std::chrono::steady_clock> detectStartTime = std::chrono::steady_clock::now();

            // 连接tcp服务端
            if (connect(sockfd, (struct sockaddr *)&targetAddr, sizeof(targetAddr)) < 0) {
                // 关闭socket
                close(sockfd);
                // 输出日志
                SPDLOG_INFO("服务器连接失败: {}", strerror(errno));
                // 判断错误是否是套接字
                if (errno == EBADF) continue;
                // 结束循环
                break;
            }

            // 关闭socket
            close(sockfd);

            // 接收到数据，计算延迟
            // 设置延迟
            result.delay = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - detectStartTime).count();
            // 结束循环
            break;
        }

        // 回调上报
        m_onDetectedCallback(std::move(result));
    });
}

HandleTCPProbeDetectIPv6::~HandleTCPProbeDetectIPv6()
{
}

}